package com.dwxt.cdms.service;

import com.dwxt.cdms.api.common.utils.StringUtil;
import com.dwxt.cdms.dao.ChunkInfoMapper;
import com.dwxt.cdms.dao.CustomerFileMapper;
import com.dwxt.cdms.entity.ChunkInfo;
import com.dwxt.cdms.entity.CustomerFile;
import com.dwxt.cdms.entity.CustomerFileExample;
import com.dwxt.cdms.entity.dto.ChunkUploadInfo;
import com.dwxt.common.base.BaseResult;
import com.dwxt.common.base.StatusCode;
import com.dwxt.common.util.PartyUtil;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.AppendFileStorageClient;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class CustomerFileService {

    @Value("${gate.file.uploadPath}")
    private String uploadPath;

    @Value("${gate.file.chunkPath}")
    private String chunkPath;

    @Value("${gate.file.mergePath}")
    private String mergePath;

    @Value("${gate.file.realPath}")
    private String realPath;

    @Value("${gate.file.server}")
    private String server;

    @Value("${fdfs.server}")
    private String fdfsServer;

    @Autowired
    private AppendFileStorageClient appendFileStorageClient;
    @Autowired
    private FastFileStorageClient fastFileStorageClient;

    @Autowired
    private CustomerFileMapper customerFileMapper;

    @Autowired
    private ChunkInfoMapper chunkInfoMapper;

    /**
     * 分片上传到FastDFS
     *
     * @param chunkUploadInfo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public BaseResult chunkUploadToDFS(ChunkUploadInfo chunkUploadInfo) throws IOException {


        Integer chunk = chunkUploadInfo.getChunk();
        Integer chunks = chunkUploadInfo.getChunks();
        String name = chunkUploadInfo.getName();

        MultipartFile file = chunkUploadInfo.getFile();

        ChunkInfo chunkInfo = chunkInfoMapper.selectByPrimaryKey(chunkUploadInfo.getId());

        //第一次上传
        if (chunk == 0) {
            if (chunkInfo == null) {
                chunkInfo = new ChunkInfo();
                StorePath storePath = appendFileStorageClient.uploadAppenderFile("file", file.getInputStream(), file.getSize(), name.substring(name.lastIndexOf(".") + 1));
                BeanUtils.copyProperties(chunkUploadInfo, chunkInfo);
                chunkInfo.setFdfsFullPath(storePath.getFullPath());
                chunkInfo.setFdfsPath(storePath.getPath());
                chunkInfo.setFdfsGroup(storePath.getGroup());
                chunkInfo.setOffset(file.getSize());
                chunkInfoMapper.insertSelective(chunkInfo);
            } else {
                return BaseResult.build(StatusCode.CURRENT_CHUNK_INVALID);
            }
        } else if (chunk > 0 && chunk < chunks) {
            if (chunkInfo == null || !chunkInfo.getChunk().equals(chunk - 1)) {
                return BaseResult.build(StatusCode.CURRENT_CHUNK_INVALID);
            }
            String path = chunkInfo.getFdfsPath();
            String fullPath = chunkInfo.getFdfsFullPath();
            String group = chunkInfo.getFdfsGroup();
            // 修改偏移量
            appendFileStorageClient.modifyFile(group, path, file.getInputStream(), file.getSize(), chunkInfo.getOffset());
            // 追加
            appendFileStorageClient.appendFile(group, path, file.getInputStream(), file.getSize());
            if (chunk == chunks - 1) {
                chunkInfoMapper.deleteByPrimaryKey(chunkUploadInfo.getId());
                return BaseResult.ok(fdfsServer + "/" + fullPath);
            } else {
                chunkInfo.setOffset(chunkInfo.getOffset() + file.getSize());
                chunkInfo.setChunk(chunkUploadInfo.getChunk());
                chunkInfoMapper.updateByPrimaryKeySelective(chunkInfo);
            }
        }
        chunkUploadInfo.setFile(null);
        return BaseResult.ok(chunkUploadInfo);
    }

    /**
     * 报存到本地服务器测试
     * @param file
     * @param customerId
     * @return
     * @throws IOException
     */
    public String saveFile(MultipartFile file, Integer customerId) throws IOException {
        log.info("保存文件开始");
        /**
         * 首先将文件缓存到服务器上
         */
        String fileName = createFileName(file.getOriginalFilename(), customerId + "");
        //String userId = PartyUtil.getCurrentUserId();
        File dir = new File(realPath + uploadPath + "/" + customerId);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        String savePath = realPath + uploadPath + "/" + customerId + "/" + fileName;
        File saveFile = new File(savePath);
        file.transferTo(saveFile);
        // 先尝试压缩并保存图片
        //Thumbnails.of(file.getInputStream()).scale(1f).outputQuality(0.25f).toFile(saveFile);
        // 设置访问权限，可读，所有人可读
        saveFile.setReadable(true, false);
        log.info("保存文件结束");
        String result = server + uploadPath + "/" + customerId + "/" + fileName;

        CustomerFile customerFile = selectByCusId(customerId);
        if (customerFile == null) {
            customerFile = new CustomerFile();
            customerFile.setCustomerId(customerId);
            customerFile.setPath(result);
            customerFileMapper.insertSelective(customerFile);
        } else {
            customerFile.setCreateTime(new Date());
            customerFileMapper.updateByPrimaryKeySelective(customerFile);
        }
        return result;

    }

    private String createFileName(String originalFilename, String fileName) {
        String result = "";
        // 获取文件后缀
        String suffix = getFileSuffix(originalFilename);
        String[] split = StringUtil.split(originalFilename,".");
//        if (StringUtils.isNotBlank(fileName)) {
//            result = fileName + "." + suffix;
//        } else {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
            String date = sdf.format(new Date());
            if (StringUtils.isNotBlank(suffix)) {
                result = split[0]+"-"+date + "." + suffix;
            }
//        }
        return result;
    }

    private String getFileSuffix(String fileName) {
        // 获取文件后缀
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        if (StringUtils.isNotBlank(suffix)) {
            suffix = suffix.toLowerCase();
        }
        return suffix;
    }

    public CustomerFile selectByCusId(Integer customerId) {
        CustomerFileExample example = new CustomerFileExample();
        example.createCriteria().andCustomerIdEqualTo(customerId);
        List<CustomerFile> customerFiles = customerFileMapper.selectByExample(example);
        return customerFiles.size() > 0 ? customerFiles.get(0) : null;
    }

    public BaseResult chunkUpload(ChunkUploadInfo chunkUploadInfo) {
        //用户上传文件被分隔的总块数
        int chunks = chunkUploadInfo.getChunks();
        //当前块，从0开始
        int nowChunk = chunkUploadInfo.getChunk();

        MultipartFile file = chunkUploadInfo.getFile();
        //原始文件名
        String originalFilename = file.getOriginalFilename();

        //第一次上传  创建文件夹   保存元数据
        if (nowChunk == 0) {
            String dir = this.realPath + this.chunkPath + System.currentTimeMillis() + "-" + originalFilename;
            chunkUploadInfo.setDir(dir);
            File chunkPathDir = new File(dir);
            if (!chunkPathDir.exists()) {
                chunkPathDir.mkdirs();
            }
            File saveFilePath = new File(dir + "/" + originalFilename + ".part" + nowChunk);
            if (saveChunkFile(saveFilePath, file)) {
                chunkInfoMapper.insert(chunkUploadInfo);
            }
            //非第一次  更新元数据
        } else if (nowChunk > 0 && nowChunk < chunks) {
            ChunkInfo chunkInfo = chunkInfoMapper.selectByPrimaryKey(chunkUploadInfo.getId());
            File saveFilePath = new File(chunkInfo.getDir() + "/" + originalFilename + ".part" + nowChunk);
            if (saveChunkFile(saveFilePath, file)) {
                chunkInfoMapper.updateByPrimaryKeySelective(chunkUploadInfo);
            }
            //最后一块上传完后合并
            if (nowChunk == chunks - 1) {
                File dir = new File(chunkInfo.getDir());
                File result = mergeFile(dir, System.currentTimeMillis() + "-" + chunkUploadInfo.getName());
                if (result.length() == chunkUploadInfo.getSize()) {
                    //删除分块文件
                    delChunkInfo(chunkInfo);
                    return BaseResult.ok(this.server + this.mergePath + "/" + result.getName());
                }
            }
        }
        chunkUploadInfo.setFile(null);
        return BaseResult.ok(chunkUploadInfo);
    }

    /**
     * 合并文件
     *
     * @param dir
     * @param fileName
     * @return
     */
    public File mergeFile(File dir, String fileName) {
        File result = null;
        if (dir.isDirectory()) {
            File[] files = dir.listFiles();
            File mergePath = new File(this.realPath + this.mergePath);
            if (!mergePath.exists()) {
                mergePath.mkdirs();
            }
            File mergeFile = new File(mergePath.getPath() + "/" + fileName);
            try (RandomAccessFile bos = new RandomAccessFile(mergeFile, "rw");) {

                for (File file : files) {
                    //移动偏移量到末尾
                    bos.seek(mergeFile.length());
                    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
                    int len = 0;
                    byte[] b = new byte[1024 * 10];
                    while ((len = bis.read(b)) != -1) {
                        bos.write(b, 0, len);
                    }
                    bis.close();
                }
                result = mergeFile;
            } catch (IOException e) {
                result = null;
                mergeFile.delete();
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 保存块文件
     *
     * @param target
     * @param multipartFile
     * @return
     */
    public boolean saveChunkFile(File target, MultipartFile multipartFile) {
        boolean result;
        try {
            FileUtils.writeByteArrayToFile(target, multipartFile.getBytes());
            result = true;
        } catch (IOException e) {
            target.delete();
            result = false;
        }
        return result;
    }

    /**
     * 获取分片信息
     *
     * @param id
     * @return
     */
    public BaseResult getChunkInfo(String id) {
        ChunkInfo chunkInfo = chunkInfoMapper.selectByPrimaryKey(id);
        if (chunkInfo == null) {
            chunkInfo = new ChunkInfo();
            chunkInfo.setId(id);
            chunkInfo.setChunk(0);
        }
        return BaseResult.ok(chunkInfo);
    }

    /**
     * 删除分片信息
     *
     * @param chunkInfo
     * @return
     */
    public boolean delChunkInfo(ChunkInfo chunkInfo) {
        //删除数据库
        int i = chunkInfoMapper.deleteByPrimaryKey(chunkInfo.getId());
        //删除分块文件
        File file = new File(chunkInfo.getDir());
        for (File eachFile : file.listFiles()) {
            eachFile.delete();
        }
        return file.delete();
    }

    /**
     * 后台上传文件
     * @param file
     * @return
     * @throws IOException
     */
    public String saveFileForInfo(MultipartFile file) throws IOException {
        log.info("保存文件开始");
        /**
         * 首先将文件缓存到服务器上
         */
        String userId = PartyUtil.getCurrentUserId();
        String fileName = createFileName(file.getOriginalFilename(),userId);
        File dir = new File(realPath + uploadPath + "/information/" + userId);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        String savePath = realPath + uploadPath + "/information/" + userId + "/" + fileName;
        File saveFile = new File(savePath);
        file.transferTo(saveFile);
        // 先尝试压缩并保存图片
        //Thumbnails.of(file.getInputStream()).scale(1f).outputQuality(0.25f).toFile(saveFile);
        // 设置访问权限，可读，所有人可读
        saveFile.setReadable(true, false);
        log.info("保存文件结束");
        String result = server + uploadPath + "/information/" + userId + "/" + fileName;
        return result;
    }
}
